import { FileUtils } from '~/core/extensions';
import { adapter } from '~/adapter/Adapter';
import { DefaultLinterOptions, LinterOptions } from './Options';
import { QVoGEngine } from '~/core/engine';
import { Queryable, QueryDescriptor } from '~/core/dsl';
import { Configuration } from '~/core/Configuration/index';
import { ArkTsSpecification } from '~/language';
import { load_rules } from './rules_loader';

const logger = Configuration.getLogger('linter');

export async function run(cwd?: string): Promise<void> {
    if (cwd) {
        // switch to the specified directory
        process.chdir(cwd);
    }
    logger.info(`Running QVoG Linter in ${process.cwd()}`);

    const options = loadOptions(`qvog.config.json`);
    parseOptions(options);

    // prepare engine
    const engine = new QVoGEngine(options.engine);
    const desc = new QueryDescriptor().withDatabase(Configuration.dbContext) as QueryDescriptor
    await desc.prepareTables()
    // const items = await Configuration.getDbContext().getGremlinConnection().source.V().range(0, 100).toList()
    // prepare rules
    const queries: Queryable[] = [];
    const rules = await load_rules()
    const enabledNativeRules: Queryable[] = []
    const allNativeRules = rules.nativeRules.sort((a, b) => -(a[0][0].priority - b[0][0].priority))
    const globalRules: Queryable[] = []

    for (const x of allNativeRules.filter(x => x[1] === 'on')) {
        const items = x[0][0].region === 'global' ? globalRules : enabledNativeRules;
        items.push(x[0]);
    }
    queries.push(...enabledNativeRules)

    const enabledDslRules = rules.dslRules.filter(x => x[1] === 'on').sort((a, b) => -(a[0][0].priority - b[0][0].priority))
    queries.push(...enabledDslRules.map(x => adapter.loadDslRule(x[0][1])));

    const total_enables = queries.length + globalRules.length
    logger.info(`Loaded ${total_enables}/${rules.dslRules.length + rules.nativeRules.length} rules`);

    const startTime = performance.now();
    // execute global rules first
    for (const rule of globalRules) {
        const callback = rule[1];
        await callback.bind(engine)(desc)
    }
    await engine.submit(queries);
    const endTime = performance.now();
    logger.info(`Executed ${total_enables} rules,spent: ${endTime - startTime} ms`);
}

function loadOptions(file: string): LinterOptions {
    if (FileUtils.exists(file)) {
        return JSON.parse(FileUtils.readTextFile(file));
    } else {
        logger.warn(`File ${file} not found. Using default options.`);
        return DefaultLinterOptions;
    }
}

function parseOptions(options: LinterOptions): void {
    switch (options.language || 'arkts') {
        case 'arkts':
            Configuration.setSpecification(ArkTsSpecification);
            break;
        default:
            throw new Error(`Unsupported language: ${options.language}`);
    }
}
